//
// vm.c
// the vm execution loop
//
// (c) 2008 why the lucky stiff, the freelance professor
//
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <math.h>
#include "potion.h"
#include "internal.h"
#include "opcodes.h"
#include "asm.h"
#include "khash.h"
#include "table.h"

extern PNTarget potion_target;

// 用于字节码模式的闭包的 .method 字段，调用闭包时：
//  运行虚表 self 中的闭包 .data[0] 处的 proto 字节码，并传入闭包中的 upval ，和如下的参数：
//  根据闭包参数表 .sig 从变长参数中收集同样数量的参数。
//  类似 potion_proto_method 。
// TODO hsq 如果实参 ... 少于形参， 要避免 va_arg 读取无效数据。
PN_MSG(potion_vm_proto, ...) {
  PN ary = PN_NIL;
  vPN(Proto) f = (struct PNProto *)PN_CLOSURE(closure)->data[0];
  if (PN_IS_TUPLE(f->sig)) {
    va_list args;
    va_start(args, self);
    ary = PN_TUP0();
    PN_TUPLE_EACH(f->sig, i, v, {
      if (PN_IS_STR(v))
        ary = PN_PUSH(ary, va_arg(args, PN));
    });
    va_end(args);
  }
  return potion_vm(P, (PN)f, self, ary,
    PN_CLOSURE(closure)->extra - 1, &PN_CLOSURE(closure)->data[1]);
}

// 创建、返回类（虚表）； closure 是构造函数 ｜ nil ， parent 是父类 。
PN potion_vm_class(Potion *P, PN closure, PN parent) {
  if (PN_TYPE(closure) == PN_TCLOSURE) {
    vPN(Proto) proto = PN_PROTO(PN_CLOSURE(closure)->data[0]);
    PN ivars = potion_tuple_with_size(P, PN_TUPLE_LEN(proto->paths));
    PN_TUPLE_EACH(proto->paths, i, v, {
      PN_TUPLE_AT(ivars, i) = PN_TUPLE_AT(proto->values, PN_INT(v));
    });
    return potion_class(P, closure, parent, ivars);
  }

  // TODO hsq closure -> PN_NIL ?
  return potion_class(P, PN_NIL, parent, closure);
}

#define STACK_MAX 4096

// 注册各个平台的 VM 回调函数： X86 、 PPC 等。
void potion_vm_init(Potion *P) {
  // TODO hsq 例如，通过类似 #define target(op) potion_x86_##op 的形式直接调用函数，而不是构造 PNTarget ？
  #if POTION_JIT == 1
    P->target = potion_target;
  #endif
}

#if POTION_JIT == 1
// 拼接的标识符无法直接跳转到定义处
// #define CASE_OP(name, args) case OP_##name: target->op[OP_##name]args; break;
#define CASE_OP(_op, args) case _op: target->op[_op]args; break;

// 编译 proto 为 PN_F 类型的汇编函数：
//  根据其栈、局部变量、 upval 等信息，先递归编译子函数，再将字节码编译成机器码，
//  整个指令序列构成的 PN_F 函数赋予 proto.jit 并返回；
// 该汇编函数被调用时，按照 PN_F 接口，先传入 3 个系统参数，再传入可选的 proto 的接口参数。
//  类似 potion_vm_proto 。
//  CL.data[0] = proto, CL.method = proto.jit 。
PN_F potion_jit_proto(Potion *P, PN proto) {
  vPN(Proto) f = (struct PNProto *)proto;
  PNAsm * volatile asmb = potion_asm_new(P);
  PNTarget *target = &P->target;

  // 被调函数的 preluge 处理
  // target->setup(TARGET_PROTO_ARGS);

  // 递归编译子函数；找到 直接子函数的最大参数数量
  long protoargs = 4; // max(4, 3 + 直接子函数的最大参数数量)
  if (PN_TUPLE_LEN(f->protos) > 0) {
    PN_TUPLE_EACH(f->protos, i, proto2, {
      int p2args = 3;
      vPN(Proto) f2 = (struct PNProto *)proto2;
      // TODO: i'm repeating this a lot. sad.
      if (PN_IS_TUPLE(f2->sig)) {
        PN_TUPLE_EACH(f2->sig, i, v, {
          if (PN_IS_STR(v)) p2args++;
        });
      }
      if (f2->jit == NULL)
        potion_jit_proto(P, proto2);
      if (p2args > protoargs)
        protoargs = p2args;
    });
  }

  int upc = UPVALSIZE(f);
  long locals = f->stacksize;
  long upvals = locals + LOCALSIZE(f);
  long sysargs = upvals + upc;
  long userargs = sysargs + 3;
  long rsp = (userargs + protoargs) * sizeof(PN);
  // 栈布局： (.stack + LOCALSIZE + UPVALSIZE + 3 + 子函数最大参数数量) 个 PN 字节
  //    系统内部 3 参数: Potion *, closure, self

  // 被调函数的 preluge 处理，分配栈空间
  target->setup(TARGET_PROTO_ARGS, rsp);
  // 分配栈空间
  // target->stack(TARGET_PROTO_ARGS, rsp);
  // 系统内部 3 参数入栈，局部变量按需置为 nil 。
  target->registers(TARGET_PROTO_ARGS, sysargs);

  // 用户参数入栈
  // Read locals
  if (PN_IS_TUPLE(f->sig)) {
    long argx = 0;
    PN_TUPLE_EACH(f->sig, i, v, {
      if (PN_IS_STR(v)) {
        PN_SIZE num = PN_GET(f->locals, v);
        target->local(TARGET_PROTO_ARGS, locals + num, argx);
        argx++;
      }
    });
  }

  // upval 入栈
  // if CL passed in with upvals, load them
  if (upc > 0)
    target->upvals(TARGET_PROTO_ARGS, upvals, sysargs + 1, upc);

  PN_SIZE posn = PN_OP_LEN(f);
  PN_SIZE jmpn = 0; // 向后跳转指令数

  for (PN_SIZE pos = 0; pos < posn; pos++) {
    switch (PN_OP_AT(f, pos).code) {
      case OP_JMP: case OP_TESTJMP: case OP_NOTJMP: {
        if (PN_OP_AT(f, pos).b >= 0) jmpn++;
      }
    }
  }

  PNJumps jmps[jmpn]; // 每个向后跳转指令：{from=要修改跳转偏移的位置, to=目标字节码位置}
  // TODO hsq 减少 posn ：提前记录作为跳转目标的字节码？
  PN_SIZE offs[posn]; // 每个指令：字节码位置 -> 机器码位置
  PN_SIZE jmpc = 0;

  for (PN_SIZE pos = 0; pos < posn; pos++) {
    offs[pos] = asmb->len;

    switch (PN_OP_AT(f, pos).code) {
      CASE_OP(OP_MOVE,     (TARGET_OP_ARGS))
      CASE_OP(OP_LOADPN,   (TARGET_OP_ARGS))
      CASE_OP(OP_LOADVAL,  (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_SELF,     (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_GETLOCAL, (TARGET_OP_ARGS, locals))
      CASE_OP(OP_SETLOCAL, (TARGET_OP_ARGS, locals))
      CASE_OP(OP_GETUPVAL, (TARGET_OP_ARGS, upvals))
      CASE_OP(OP_SETUPVAL, (TARGET_OP_ARGS, upvals))
      CASE_OP(OP_NEWTUPLE, (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_SETTUPLE, (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_SETTABLE, (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_NEWLICK,  (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_GETPATH,  (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_SETPATH,  (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_ADD,      (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_SUB,      (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_MULT,     (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_DIV,      (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_REM,      (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_POW,      (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_NEQ,      (TARGET_OP_ARGS))
      CASE_OP(OP_EQ,       (TARGET_OP_ARGS))
      CASE_OP(OP_LT,       (TARGET_OP_ARGS))
      CASE_OP(OP_LTE,      (TARGET_OP_ARGS))
      CASE_OP(OP_GT,       (TARGET_OP_ARGS))
      CASE_OP(OP_GTE,      (TARGET_OP_ARGS))
      CASE_OP(OP_BITN,     (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_BITL,     (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_BITR,     (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_PIPE,     (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_CARET,    (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_AMP,      (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_DEF,      (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_BIND,     (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_MESSAGE,  (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_JMP,      (TARGET_OP_ARGS, jmps, offs, &jmpc))
      CASE_OP(OP_TEST,     (TARGET_OP_ARGS))
      CASE_OP(OP_NOT,      (TARGET_OP_ARGS))
      CASE_OP(OP_CMP,      (TARGET_OP_ARGS))
      CASE_OP(OP_TESTJMP,  (TARGET_OP_ARGS, jmps, offs, &jmpc))
      CASE_OP(OP_NOTJMP,   (TARGET_OP_ARGS, jmps, offs, &jmpc))
      CASE_OP(OP_NAMED,    (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_CALL,     (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_CALLSET,  (TARGET_OP_ARGS, sysargs))
      CASE_OP(OP_RETURN,   (TARGET_OP_ARGS))
      CASE_OP(OP_PROTO,    (TARGET_PROTO_ARGS, &pos, upvals, sysargs, locals))
      CASE_OP(OP_CLASS,    (TARGET_OP_ARGS, sysargs))
    }
  }

  for (PN_SIZE jmpi = 0; jmpi < jmpc; jmpi++) {
    unsigned char *asmj = asmb->ptr + jmps[jmpi].from;
    target->jmpedit(TARGET_PROTO_ARGS, asmj, offs[jmps[jmpi].to] - (jmps[jmpi].from + 4));
  }

  // 空。
  target->finish(TARGET_PROTO_ARGS);

  #ifdef JIT_DEBUG
    fprintf(stderr, "{ n = 0x%x, p = 0x%x,\n", f->uniq,
      PN_IS_PROTO(f->source) ? PN_PROTO(f->source)->uniq : 0);

    fprintf(stderr, "stack: reg(0) locals(%ld) upvals(%ld) sysargs(%ld) userargs(%ld) protoargs(#%ld)\n",
            locals, upvals, sysargs, userargs, protoargs);

    fprintf(stderr, "op2jit-off {{{\n");
    for (long ai = 0; ai < posn; ai++) {
      fprintf(stderr, "%03ld : %04x\n", ai, offs[ai]);
    }
    fprintf(stderr, "op2jit-off }}}\n");

    // for (long ai = 0; ai < asmb->len; ai++) {
    //   fprintf(stderr, "0x%02x, %s", asmb->ptr[ai], (ai % 16 == 15 ? "\n" : (ai % 8 == 7 ? "\t" : "")));
    // }
    fprintf(stderr, "\n}\n");
  #endif

  u8 *fn = PN_ALLOC_FUNC(JIT_LEN_SIZE + asmb->len);
  PN_MEMCPY_N(fn, &(asmb->len), u8, JIT_LEN_SIZE);
  PN_MEMCPY_N(fn + JIT_LEN_SIZE, asmb->ptr, u8, asmb->len);
  PN_EXEC_FUNC(fn, asmb->len);

  return f->jit = (PN_F)fn;
}
#endif

// 执行二元数学运算，双整数直接解码计算，否则向首参发运算符消息。
#define PN_VM_MATH(name, oper) \
  if (PN_IS_NUM(reg[op.a]) && PN_IS_NUM(reg[op.b])) \
    reg[op.a] = PN_NUM(PN_INT(reg[op.a]) oper PN_INT(reg[op.b])); \
  else \
    reg[op.a] = potion_obj_##name(P, reg[op.a], reg[op.b]);

// 调用 PNProto proto ，传入参数表 vargs 、 upval 表 upargs[upc] ，返回执行结果；
// vargs: 元组 | nil ，
// TODO hsq 参数类型都是 PN ，怎样做严格的编译时或运行时限制、检查？
PN potion_vm(Potion *P, PN proto, PN self, PN vargs, PN_SIZE upc, PN *upargs) {
  vPN(Proto) f = (struct PNProto *)proto;

  // these variables persist as we jump around
  PN stack[STACK_MAX];

  // these variables change from proto to proto
  // current = upvals | locals | self | reg
  PN_SIZE pos = 0;
  PN *args = NULL, *upvals, *locals, *reg;
  PN *current = stack;
  // pos: 指令索引， args: 参数指针，
  // current: 栈指针， 栈帧布局依次为 upval* local* self reg(self) ，
  //    之前 2 寄存器分别是前一栈帧的 proto 和 pos
  // TODO hsq reg: 作为临时变量、中间变量？

  if (vargs != PN_NIL) args = PN_GET_TUPLE(vargs)->set;
reentry:
  if (current - stack >= STACK_MAX) {
    fprintf(stderr, "all registers used up!");
    exit(1);
  }

  upvals = current;
  locals = upvals + UPVALSIZE(f);
  reg = locals + LOCALSIZE(f) + 1;

  // 用调用者传入的参数初始化 callinfo 栈帧中的 uplval 和 local 。
  // TODO hsq 为什么要通过栈来传递、操作 upval ，而不是直接访问闭包？
  //    以及 PNWeakRef 存在的必要性？
  // TODO hsq 是否需要检查越界，如 min(upc, UPVALSIZE(f)) ，或者调用前已经检查？
  // TODO hsq 栈帧是否初始化为 nil ？会影响未初始化的 args upval local 。
  if (pos == 0) {
    reg[-1] = reg[0] = self;
    // 传入 upval 列表
    if (upc > 0 && upargs != NULL) {
      for (PN_SIZE i = 0; i < upc; i++) {
        upvals[i] = upargs[i];
      }
    }

    if (args != NULL) {
      // 传入 参数表
      // TODO hsq .sig 和 .locals 都是元组，可以直接按顺序传入各个参数？
      //    或者记录参数数量，直接从 .locals 中顺序取？
      // TODO hsq 调用闭包时，参数直接入栈，而不是先收集到一个临时元组中？
      PN_TUPLE_EACH(f->sig, i, v, {
        if (PN_IS_STR(v)) {
          PN_SIZE num = PN_GET(f->locals, v);
          locals[num] = *args++;
        }
      });
    }
  }

  while (pos < PN_OP_LEN(f)) {
    #define CASE(OP_AND_CODE) case OP_AND_CODE; break
    PN_OP op = PN_OP_AT(f, pos);
    switch (op.code) {
      CASE(OP_MOVE:    reg[op.a] = reg[op.b]);
      CASE(OP_LOADVAL: reg[op.a] = PN_TUPLE_AT(f->values, op.b));
      CASE(OP_LOADPN:  reg[op.a] = (PN)op.b);
      CASE(OP_SELF:    reg[op.a] = reg[-1]);
      CASE(OP_GETLOCAL: {
          if (PN_IS_REF(locals[op.b]))
            reg[op.a] = PN_DEREF(locals[op.b]);
          else
            reg[op.a] = locals[op.b];
        });
      CASE(OP_SETLOCAL: {
          if (PN_IS_REF(locals[op.b])) {
            PN_DEREF(locals[op.b]) = reg[op.a];
            PN_TOUCH(locals[op.b]);
          } else
            locals[op.b] = reg[op.a];
        });
      CASE(OP_GETUPVAL: reg[op.a] = PN_DEREF(upvals[op.b]));
      CASE(OP_SETUPVAL: {
          PN_DEREF(upvals[op.b]) = reg[op.a];
          PN_TOUCH(upvals[op.b]);
        });
      CASE(OP_NEWTUPLE: reg[op.a] = PN_TUP0());
      CASE(OP_SETTUPLE: reg[op.a] = PN_PUSH(reg[op.a], reg[op.b]));
      CASE(OP_SETTABLE: potion_table_set(P, reg[op.a], reg[op.a + 1], reg[op.b]));
      CASE(OP_NEWLICK: {
          PN inner = op.b > op.a ? reg[op.a + 1] : PN_NIL;
          PN attr = op.b > op.a + 1 ? reg[op.b] : PN_NIL;
          reg[op.a] = potion_lick(P, reg[op.a], inner, attr);
        });
      CASE(OP_GETPATH: reg[op.a] = potion_obj_get(P, reg[op.a], reg[op.b]));
      CASE(OP_SETPATH: potion_obj_set(P, reg[op.a], reg[op.a + 1], reg[op.b]));
      CASE(OP_ADD:     PN_VM_MATH(add,  +));
      CASE(OP_SUB:     PN_VM_MATH(sub,  -));
      CASE(OP_MULT:    PN_VM_MATH(mult, *));
      CASE(OP_DIV:     PN_VM_MATH(div,  /));
      CASE(OP_REM:     PN_VM_MATH(rem,  %));
      CASE(OP_POW: {
          reg[op.a] = PN_NUM((int)pow((double)PN_INT(reg[op.a]),
                                      (double)PN_INT(reg[op.b])));
        });
      CASE(OP_NOT: reg[op.a] = PN_BOOL(!PN_TEST(reg[op.a])));
      // TODO hsq 下面的几个比较操作，直接作用于立即数、指针，而非指向的内容。
      CASE(OP_CMP: {
          // reg[op.a] = PN_NUM(PN_INT(reg[op.b]) - PN_INT(reg[op.a]));
          // reg[op.a] = PN_NUM(PN_INT(reg[op.a]) - PN_INT(reg[op.b]));
          long a = reg[op.a]; // PN 是无符号
          long b = reg[op.b];
          reg[op.a] = PN_NUM(a == b ? 0 : (a > b ? 1 : -1));
        });
      CASE(OP_NEQ: reg[op.a] = PN_BOOL(reg[op.a] != reg[op.b]));
      CASE(OP_EQ:  reg[op.a] = PN_BOOL(reg[op.a] == reg[op.b]));
      CASE(OP_LT:  reg[op.a] = PN_BOOL((long)(reg[op.a]) <  (long)(reg[op.b])));
      CASE(OP_LTE: reg[op.a] = PN_BOOL((long)(reg[op.a]) <= (long)(reg[op.b])));
      CASE(OP_GT:  reg[op.a] = PN_BOOL((long)(reg[op.a]) >  (long)(reg[op.b])));
      CASE(OP_GTE: reg[op.a] = PN_BOOL((long)(reg[op.a]) >= (long)(reg[op.b])));
      CASE(OP_BITN: reg[op.a] = PN_IS_NUM(reg[op.a]) ? PN_NUM(~PN_INT(reg[op.a]))
                                                     : potion_obj_bitn(P, reg[op.a]));
      CASE(OP_BITL:  PN_VM_MATH(bitl,  <<));
      CASE(OP_BITR:  PN_VM_MATH(bitr,  >>));
      CASE(OP_PIPE:  PN_VM_MATH(pipe,  | ));
      CASE(OP_CARET: PN_VM_MATH(caret, ^ ));
      CASE(OP_AMP:   PN_VM_MATH(amp,   & ));
      CASE(OP_DEF: reg[op.a] = potion_def_method(P, PN_NIL, reg[op.a], reg[op.a+1], reg[op.b]));
      CASE(OP_BIND:    reg[op.a] = potion_bind(P, reg[op.b], reg[op.a]));
      CASE(OP_MESSAGE: reg[op.a] = potion_message(P, reg[op.b], reg[op.a]));
      CASE(OP_JMP:     pos += op.b);
      // TODO hsq 对 nil(0) true(0b0110) false(0b0010) 进行优化？以及 OP_NOT ；
      // TODO hsq gcc -O2 将这行生成怎样的汇编代码？
      CASE(OP_TEST:    reg[op.a] = PN_BOOL(PN_TEST(reg[op.a])));
      CASE(OP_TESTJMP: if (PN_TEST(reg[op.a])) pos += op.b);
      CASE(OP_NOTJMP:  if (!PN_TEST(reg[op.a])) pos += op.b);
      CASE(OP_NAMED: {
          // TODO hsq 可消除查找，在编译时查找、生成参数入栈指令？见 PN_ARG_TABLE 。
          // TODO hsq 没找到应该报错？
          int x = potion_sig_find(P, reg[op.a], reg[op.b - 1]);
          if (x >= 0) reg[op.a + x + 2] = reg[op.b];
        });
      case OP_CALL:
        #define CL PN_CLOSURE(reg[op.a])
        switch (PN_TYPE(reg[op.a])) {
          case PN_TVTABLE:
            reg[op.a + 1] = potion_object_new(P, PN_NIL, reg[op.a]);
            reg[op.a] = ((struct PNVtable *)reg[op.a])->ctor;
            // TODO hsq 若 .ctor 非 CL ？
            // if (PN_TYPE(reg[op.a]) != PN_TCLOSURE) break;
            // TODO hsq 避免 warning: this statement may fall through [-Wimplicit-fallthrough=]
            if (PN_TYPE(reg[op.a]) == PN_TCLOSURE) goto CALL_CLOSURE;
            break;
          case PN_TCLOSURE:
          CALL_CLOSURE:
            if (CL->method != (PN_F)potion_vm_proto) {
              // TODO hsq 用于什么情况？
              reg[op.a] = potion_call(P, reg[op.a], op.b - op.a, reg + op.a + 1);
            } else if (((reg - stack) + f->stacksize + UPVALSIZE(f) + LOCALSIZE(f) + 8) >= STACK_MAX) {
              // 栈帧不够这次调用所需，则进入新的 potion_vm 调用产生新栈
              int i;
              PN argt = potion_tuple_with_size(P, (op.b - op.a) - 1);
              for (i = 2; i < op.b - op.a; i++)
                PN_TUPLE_AT(argt, i - 2) = reg[op.a + i];
              reg[op.a] = potion_vm(P, CL->data[0], reg[op.a + 1], argt, CL->extra - 1, &CL->data[1]);
            } else {
              self   = reg[op.a  + 1];
              args   = &reg[op.a + 2];
              upc    = CL->extra - 1;
              upargs = &CL->data[1];
              current     = reg + f->stacksize + 2;
              current[-2] = (PN)f;
              current[-1] = (PN)pos;

              f = PN_PROTO(CL->data[0]);
              pos = 0;
              goto reentry;
            }
          break;

          default: {
            reg[op.a + 1] = reg[op.a];
            reg[op.a] = potion_obj_get_call(P, reg[op.a]);
            if (PN_IS_CLOSURE(reg[op.a]))
              reg[op.a] = potion_call(P, reg[op.a], op.b - op.a, &reg[op.a + 1]);
          }
          break;
        }
        #undef CL
        break;
      CASE(OP_CALLSET: reg[op.a] = potion_obj_get_callset(P, reg[op.b]));
      CASE(OP_RETURN:
        if (current != stack) {
          PN val = reg[op.a];

          f = PN_PROTO(current[-2]);
          pos = (PN_SIZE)current[-1];
          op = PN_OP_AT(f, pos);

          reg = current - (f->stacksize + 2);
          current = reg - (LOCALSIZE(f) + UPVALSIZE(f) + 1);
          reg[op.a] = val;
          pos++;
          goto reentry;
        } else {
          reg[0] = reg[op.a];
          goto done;
        });
      CASE(OP_PROTO: ({
          vPN(Closure) cl;
          proto = PN_TUPLE_AT(f->protos, op.b);
          cl = (struct PNClosure *)potion_closure_new(P, (PN_F)potion_vm_proto,
            PN_PROTO(proto)->sig, PN_TUPLE_LEN(PN_PROTO(proto)->upvals) + 1);
          cl->data[0] = proto;
          reg[op.a] = (PN)cl;
          PN_TUPLE_COUNT(PN_PROTO(proto)->upvals, i, {
            pos++;
            op = PN_OP_AT(f, pos);

            if (op.code == OP_GETUPVAL) {
              cl->data[i+1] = upvals[op.b];
            } else if (op.code == OP_GETLOCAL) {
              cl->data[i+1] = locals[op.b] = (PN)potion_ref(P, locals[op.b]);
            } else {
              fprintf(stderr, "** missing an upval to proto %p\n", (void *)proto);
            }
          });
        }));
      // TODO hsq 若以闭包为父类创建无构造函数的子类，op.b==op.a ，闭包本身被作为构造函数？结果如何？
      CASE(OP_CLASS: reg[op.a] = potion_vm_class(P, reg[op.b], reg[op.a]));
    }
    pos++;
    #undef CASE
  }

  done:
  return reg[0];
}
